package com.goodsogood.union.auth.aspect;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.goodsogood.union.auth.aspect.vo.LogData;
import com.goodsogood.union.auth.conf.LogConfiger;

/**
 * 访问日志
 */
@Component
@Aspect
public class LogAspectAtController {

    final Logger logger = LoggerFactory.getLogger(LogConfiger.ACCESS);
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Autowired
    private LogChain logChain;

    public static final String getIpAddr(HttpServletRequest request) {
        String ipString = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getRemoteAddr();
        }
        // 多个路由时，取第一个非unknown的ip
        final String[] arr = ipString.split(",");
        for (final String str : arr) {
            if (!"unknown".equalsIgnoreCase(str)) {
                ipString = str;
                break;
            }
        }
        return ipString;
    }

    /**
     * 一个接入点
     */
    @Pointcut("execution(* com.goodsogood.union.auth.web.controller..*.*(..))")
    public void controllerJointcut() {
    }
    
    /**
     * @Description: 方法调用前触发
     */
    @Before("controllerJointcut()")
    public void doBeforeInControllerLayer(JoinPoint joinPoint) {
        LogData logData = logChain.getLogData();
        logData.setRequestStart(System.currentTimeMillis());// 记录方法开始执行的时间
    }

    /**
     * @Description: 方法调用后触发
     */
    @After("controllerJointcut()")
    public void doAfterInControllerLayer(JoinPoint joinPoint) {
        LogData logData = logChain.getLogData();
        logData.setRequestEnd(System.currentTimeMillis());// 记录方法执行完成的时间
        this.printOptLog();
    }

    /**
     * @Description: 环绕触发
     */
    @Around("controllerJointcut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        
        LogData logData = logChain.getLogData();
        
        // 获取请求地址
        String requestPath = request.getRequestURI();
        logData.setUrl(requestPath);
        logData.setIp(getIpAddr(request));
        logData.setUserAgent(request.getHeader("User-Agent"));
        logData.setMethod(request.getMethod());
        
        //过滤输入
        Object inputParamMap = null;
        if(urlInPutFilter.contains(requestPath)) {
            //不打印输入
            inputParamMap = "request is filtred";
        } else {
            // 获取输入参数
            Object[] objs = pjp.getArgs();
            if(objs!=null&&objs.length!=0){
                inputParamMap = objs[0];
            }
        }
        
        // 执行完方法的返回值：调用proceed()方法，就会触发切入点方法执行
        Map<String, Object> outputParamMap = new HashMap<>();

        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            logData.setException(e.getMessage());
            throw e;  //原路抛出
        } finally {
            //过滤响应
            if(urlOutPutFilter.contains(requestPath)) {
                outputParamMap.put("result", "response is filtered");
            } else {
                outputParamMap.put("result", result);
            }
            
            try {
                logData.setRequest(JSON.toJSONString(JSON.toJSON(inputParamMap)));
                logData.setResponse(JSON.toJSONString(outputParamMap));
            } catch (Exception e) {
            }
        }
        
        return result;
    }

    /**
     *
     * @Title：printOptLog
     * @Description: 输出日志
     */
    private void printOptLog() {
        try {
            
            LogData logData = logChain.getLogData();
            
            StringBuilder output = new StringBuilder();
            output.append("\nip:\t\t\t").append(logData.getIp());
            output.append("\nurl:\t\t").append(logData.getUrl());
            output.append("\ntime:\t\t").append(sdf.format(logData.getRequestStart()));
            output.append("\ncost:\t\t").append((logData.getRequestEnd() - logData.getRequestStart())).append("ms");
            output.append("\nrequest:\t").append(logData.getRequest());
            output.append("\nresponse:\t").append(logData.getResponse());
            logger.info(output.toString());
        } catch(Exception e) {
        }
    }
    
    private final static List<String> urlInPutFilter = Arrays.asList(
          "/njr/m/updateUserHeadBase64",     // 上传用户头像
          "/njr//m/updateUserHeadBase64",
          "/njr/address/china",
          "/njr/yzmtest",
          "/njr/yzm");

  private final static List<String> urlOutPutFilter = Arrays.asList(
          "/njr/address/china",
          "/njr/report",
          "/njr/dataDict/list");
}
